package personnel.department.pages.addpage;

import static personnel.department.utils.ReflectionUtils.getGenericClass;

import java.lang.reflect.Field;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.Widget;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

import personnel.department.dao.entity.Career;
import personnel.department.dao.entity.Education;
import personnel.department.dao.entity.Employee;
import personnel.department.dao.entity.UniqueObject;
import personnel.department.pages.PDTable;
import personnel.department.pages.TwoColumnFieldsComposite;
import personnel.department.pages.constants.Icons;
import personnel.department.pages.widgets.AbstractWidget;
import personnel.department.utils.ReflectionUtils;
import personnel.department.utils.TranslatorUtils;

public abstract class AbstractTableEditor<T extends UniqueObject> {

	private CTabFolder tabFolder;
	private String title;
	private PDTable<T> table;
	protected List<Pair<String, AbstractWidget<?>>> widgetByName;
	protected SimpleDateFormat format;
	private Set<T> editorObjects = Collections.emptySet();
	private Class<T> genericClass;
	private Button addButton;
	private Button saveButton;

	public AbstractTableEditor(CTabFolder tabFolder, String title) {
		this.tabFolder = tabFolder;
		this.title = title;
		this.format = new SimpleDateFormat("dd/MM/yyyy");
		genericClass = (Class<T>) getGenericClass(getClass().getGenericSuperclass());
	}

	public AbstractTableEditor(CTabFolder tabFolder, String title, Set<T> editorObjects) {
		this(tabFolder, title);
		this.editorObjects = editorObjects;
	}

	public abstract PDTable<T> createTable(Composite parent);

	public Set<T> getAllInstances() {
		return Sets.newLinkedHashSet(table.getModelProvider());
	}

	protected List<T> getModel() {
		return Lists.newArrayList(editorObjects);
	}

	public void createContent() {
		CTabItem certificateitem = new CTabItem(tabFolder, SWT.NONE);
		certificateitem.setText(title);

		Composite composite = new Composite(tabFolder, SWT.NONE);
		composite.setLayout(new GridLayout());
		composite.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create());

		createTitleComposite(composite);

		createTableComposite(composite);



		TwoColumnFieldsComposite fieldsComposite = new TwoColumnFieldsComposite(composite, getCorrectFields(), SWT.NONE);
		fieldsComposite.createControl();
		widgetByName = fieldsComposite.getWidgetByName();

		certificateitem.setControl(composite);
		validate();
	}

	protected void validate() {}

	private List<Field> getCorrectFields() {
		Class<?> genericClass = ReflectionUtils.getGenericClass(getClass().getGenericSuperclass());
		List<String> filedNames = TranslatorUtils.getFiledNamesByType(genericClass);
		List<Field> fields = TranslatorUtils.getTranslatorFields(genericClass.getDeclaredFields(), Employee.class, Education.class, Career.class);
		List<Field> translatorFields = Lists.newArrayList();
		for (Field field : fields) {
			if (filedNames.contains(field.getName())) {
				translatorFields.add(field);
			}
		}
		return translatorFields;
	}

	private void createTableComposite(Composite composite) {
		Composite tableComposite = new  Composite(composite, SWT.NONE);
		tableComposite.setLayout(new GridLayout());
		tableComposite.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).hint(SWT.DEFAULT, 150).create());
		table = createTable(tableComposite);
		table.createTable();
		TableViewer viewer = table.getViewer();
		viewer.addDoubleClickListener(event -> {
			IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
			T currentObject = (T) selection.getFirstElement();

			allWidgetEnabled();

			initWidgets(currentObject);
			addButton.setEnabled(false);
			saveButton.setEnabled(true);

		});
	}

	private void allWidgetEnabled() {
		widgetByName.stream()
		.map(pair -> pair.getRight())
		.forEach(widget -> widget.setEnabled(true));
	}

	private void createTitleComposite(Composite composite) {
		Composite titleComposite = new Composite(composite, SWT.NONE);
		titleComposite.setLayout(new GridLayout(4, false));
		titleComposite.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create());

		Label titleTab = new Label(titleComposite, SWT.NONE);
		titleTab.setText(title);


		addButton = new Button(titleComposite, SWT.PUSH);
		addButton.setImage(new Image(addButton.getDisplay(), Icons.ADD_ICON.getPath()));
		addButton.setText("Добавить запись");
		addButton.setLayoutData(GridDataFactory.swtDefaults().align(SWT.END, SWT.CENTER).create());
		addButton.addListener(SWT.Selection, arg0 -> {
			T instance = null;
			try {
				instance = (T) TranslatorUtils.getObjectByWidgets(widgetByName, genericClass);
				if (!commitValidate(instance)) {
					return;
				}
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			table.addElement(instance);
			validate();
		});

		saveButton = new Button(titleComposite, SWT.PUSH);
		saveButton.setImage(new Image(saveButton.getDisplay(), Icons.SAVE_ICON.getPath()));
		saveButton.setText("Сохранить");
		saveButton.setLayoutData(GridDataFactory.swtDefaults().align(SWT.END, SWT.CENTER).create());
		saveButton.setEnabled(false);
		saveButton.addListener(SWT.Selection, event -> {
			IStructuredSelection selection = (IStructuredSelection) table.getViewer().getSelection();
			T currentObject = (T) selection.getFirstElement();
			initObject(currentObject);
			table.getViewer().update(currentObject, null);
			addButton.setEnabled(true);
			saveButton.setEnabled(false);
			validate();
		});
		
		Button removeButton = new Button(titleComposite, SWT.PUSH);
		removeButton.setImage(new Image(removeButton.getDisplay(), Icons.REMOVE_ICON.getPath()));
		removeButton.setText("Удалить");
		removeButton.addListener(SWT.Selection, new Listener() {
			
			@Override
			public void handleEvent(Event event) {
				IStructuredSelection selection = (IStructuredSelection) table.getViewer().getSelection();
				T currentObject = (T) selection.getFirstElement();
				List<T> provider = Lists.newArrayList(table.getModelProvider());
				provider.remove(currentObject);
				table.clear();
				table.addElements(provider);
			}
		});
	}

	private boolean commitValidate(T instance) {
		return true;
	}

	private void initWidgets(T currentObject) {
		for (Pair<String,AbstractWidget<?>> pair : widgetByName) {
			try {
				pair.getRight().setValueByObject(currentObject);
			} catch (Exception e) {
				MessageDialog.openError(tabFolder.getShell(), "Ошибка инициализации", String.format("Не удалось проинициализировать поле %s", pair.getLeft()));
			}
		}
	}


	private void initObject(T currentObject) {
		for (Pair<String,AbstractWidget<?>> pair : widgetByName) {
			try {
				pair.getRight().setValueForObject(currentObject);
			} catch (Exception e) {
				MessageDialog.openError(tabFolder.getShell(), "Ошибка инициализации", String.format("Не удалось проинициализировать поле %s", pair.getLeft()));
			}
		}
	}

	protected TableViewerColumn createTableViewerColumn(TableViewer viewer, String title, int bound, final int colNumber) {
		final TableViewerColumn viewerColumn = new TableViewerColumn(viewer,
				SWT.NONE);
		final TableColumn column = viewerColumn.getColumn();
		column.setText(title);
		column.setWidth(bound);
		column.setResizable(true);
		column.setMoveable(true);
		return viewerColumn;
	}

	protected AbstractWidget<?> getWidgetByFieldName(String fieldName) {
		return widgetByName.stream()
				.map(pair -> pair.getRight())
				.filter(widget -> widget.getFieldName().equals(fieldName))
				.findAny()
				.get();
	}
	
	public void resetWidget() {
		widgetByName.stream()
		.map(pair -> pair.getRight())
		.forEach(widget -> widget.reset());
	}
	
	public PDTable<T> getTable() {
		return table;
	}
}
